var Cartesian3 = require('./Cartesian3');
var Cartographic = require('./Cartographic');
var defaultValue = require('./defaultValue');
var defined = require('./defined');
var defineProperties = require('./defineProperties');
var DeveloperError = require('./DeveloperError');
var Ellipsoid = require('./Ellipsoid');

'use strict';

/**
 * A simple map projection where longitude and latitude are linearly mapped to X and Y by multiplying
 * them by the {@link Ellipsoid#maximumRadius}.  This projection
 * is commonly known as geographic, equirectangular, equidistant cylindrical, or plate carrée.  It
 * is also known as EPSG:4326.
 *
 * @alias GeographicProjection
 * @constructor
 *
 * @param {Ellipsoid} [ellipsoid=Ellipsoid.WGS84] The ellipsoid.
 *
 * @see WebMercatorProjection
 */
function GeographicProjection(ellipsoid) {
    this._ellipsoid = defaultValue(ellipsoid, Ellipsoid.WGS84);
    this._semimajorAxis = this._ellipsoid.maximumRadius;
    this._oneOverSemimajorAxis = 1.0 / this._semimajorAxis;
}

defineProperties(GeographicProjection.prototype, {
    /**
     * Gets the {@link Ellipsoid}.
     *
     * @memberof GeographicProjection.prototype
     *
     * @type {Ellipsoid}
     * @readonly
     */
    ellipsoid: {
        get: function () {
            return this._ellipsoid;
        }
    }
});

/**
 * Projects a set of {@link Cartographic} coordinates, in radians, to map coordinates, in meters.
 * X and Y are the longitude and latitude, respectively, multiplied by the maximum radius of the
 * ellipsoid.  Z is the unmodified height.
 *
 * @param {Cartographic} cartographic The coordinates to project.
 * @param {Cartesian3} [result] An instance into which to copy the result.  If this parameter is
 *        undefined, a new instance is created and returned.
 * @returns {Cartesian3} The projected coordinates.  If the result parameter is not undefined, the
 *          coordinates are copied there and that instance is returned.  Otherwise, a new instance is
 *          created and returned.
 */
GeographicProjection.prototype.project = function (cartographic, result) {
    // Actually this is the special case of equidistant cylindrical called the plate carree
    var semimajorAxis = this._semimajorAxis;
    var x = cartographic.longitude * semimajorAxis;
    var y = cartographic.latitude * semimajorAxis;
    var z = cartographic.height;

    if (!defined(result)) {
        return new Cartesian3(x, y, z);
    }

    result.x = x;
    result.y = y;
    result.z = z;
    return result;
};

/**
 * Unprojects a set of projected {@link Cartesian3} coordinates, in meters, to {@link Cartographic}
 * coordinates, in radians.  Longitude and Latitude are the X and Y coordinates, respectively,
 * divided by the maximum radius of the ellipsoid.  Height is the unmodified Z coordinate.
 *
 * @param {Cartesian3} cartesian The Cartesian position to unproject with height (z) in meters.
 * @param {Cartographic} [result] An instance into which to copy the result.  If this parameter is
 *        undefined, a new instance is created and returned.
 * @returns {Cartographic} The unprojected coordinates.  If the result parameter is not undefined, the
 *          coordinates are copied there and that instance is returned.  Otherwise, a new instance is
 *          created and returned.
 */
GeographicProjection.prototype.unproject = function (cartesian, result) {
    //>>includeStart('debug', pragmas.debug);
    if (!defined(cartesian)) {
        throw new DeveloperError('cartesian is required');
    }
    //>>includeEnd('debug');

    var oneOverEarthSemimajorAxis = this._oneOverSemimajorAxis;
    var longitude = cartesian.x * oneOverEarthSemimajorAxis;
    var latitude = cartesian.y * oneOverEarthSemimajorAxis;
    var height = cartesian.z;

    if (!defined(result)) {
        return new Cartographic(longitude, latitude, height);
    }

    result.longitude = longitude;
    result.latitude = latitude;
    result.height = height;
    return result;
};

module.exports = GeographicProjection;
